#include <errno.h>
#include <libgen.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/can.h>

#include "common.h"
#include "com_hud_easynavi_CanBusNative.h"


typedef struct CanBusStatus{
    int receive_bytes;
    bool running;
}CAN_BUS;

// processing callback to handler class
typedef struct {
    JavaVM  *javaVM;
    CAN_BUS  can_bus[2];
    jclass   jniCanDataCallbackClz;
    jobject  jniCanDataListeners[2];
    jmethodID midOnCanBusDataReceive;
} CAN_Context;

CAN_Context g_ctx;

JNIEXPORT void JNICALL Java_com_hud_easynavi_CanBusNative_startCanBus
  (JNIEnv *env, jclass cls, jint channel_id, jobject listener)
{
    if(channel_id < 0 || channel_id > 1) // channel_id 不能错
        return;
    LOGD("Start canBus Native() 1");
    int n = 0;
    int nbytes,i;
    struct ifreq ifr;
    FILE *out = stdout;
    struct can_frame frame;
    char *interface;

    if(channel_id == 0){
        interface = "can0";
    }else if(channel_id == 1){
        interface = "can1";
    }

    struct sockaddr_can addr;
    char buf[256];
    int	s = -1;

    int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;

    if ((s = socket(family, type, proto)) < 0) {
        LOGD("strerror: %s\n", strerror(errno));
        return;
    }

    addr.can_family = family;
    strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
    if (ioctl(s, SIOCGIFINDEX, &ifr)) {
        LOGD("strerror: %s\n", strerror(errno));
        return;
    }
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        LOGD("strerror: %s\n", strerror(errno));
        return;
    }

    jbyte aByte[8];
    jbyteArray jbarray = env->NewByteArray(8);

    g_ctx.can_bus[channel_id].running = true;
    while (g_ctx.can_bus[channel_id].running)
    {
        if ((nbytes = read(s, &frame, sizeof(struct can_frame))) < 0) {
            LOGD("strerror: %s line:%d\n", strerror(errno),__LINE__);
            return;
        }else {
            //LOGD("canbus received can_dlc:%d [%d bytes] line:%d\n",frame.can_dlc, nbytes,__LINE__);
            //if( frame.can_dlc == 8)
            memset(aByte,0,8);
            {
                aByte[0] = frame.data[0];
                aByte[1] = frame.data[1];
                aByte[2] = frame.data[2];
                aByte[3] = frame.data[3];
                aByte[4] = frame.data[4];
                aByte[5] = frame.data[5];
                aByte[6] = frame.data[6];
                aByte[7] = frame.data[7];
                env->SetByteArrayRegion(jbarray, 0, 8, aByte);
                env->CallVoidMethod(listener, g_ctx.midOnCanBusDataReceive,channel_id,frame.can_id,jbarray);
            }
//            {
//                aByte[7] = frame.data[0];
//                aByte[6] = frame.data[1];
//                aByte[5] = frame.data[2];
//                aByte[4] = frame.data[3];
//                aByte[3] = frame.data[4];
//                aByte[2] = frame.data[5];
//                aByte[1] = frame.data[6];
//                aByte[0] = frame.data[7];
//                env->SetByteArrayRegion(jbarray, 0, 8, aByte);
//                env->CallVoidMethod(listener, g_ctx.midOnCanBusDataReceive,channel_id,frame.can_id,jbarray);
//            }
        }
    }
    env->DeleteLocalRef(jbarray);
    close(s);
}

/*
 * Class:     com_hud_easynavi_CanBusNative
 * Method:    stopCanBus
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_hud_easynavi_CanBusNative_stopCanBus
  (JNIEnv *env, jclass cls, jint channel_id)
{
  if(channel_id < 0 || channel_id > 1)
    return;
  LOGD("stopping can %d\n",channel_id);
  g_ctx.can_bus[channel_id].running = false;
}


static void loadJavaMethods(JNIEnv *env){
    jclass clss = env->FindClass("com/hud/easynavi/CanBusDataListener");
    if (clss == NULL)
        abort();
    LOGD("loading  CanBusDataListener class ok");
    g_ctx.midOnCanBusDataReceive = env->GetMethodID(clss,"onCanBusDataReceive","(II[B)V"); // 回调方法
    LOGD("find method onCanBusDataReceive ok");
}


/*
 * processing one time initialization:
 *     Cache the javaVM into our context
 *     Find class ID for JniHelper
 *     Create an instance of JniHelper
 *     Make global reference since we are using them from a native thread
 * Note:
 *     All resources allocated here are never released by application
 *     we rely on system to free all global refs when it goes away;
 *     the pairing function JNI_OnUnload() never gets called at all.
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    memset(&g_ctx, 0, sizeof(g_ctx));

    g_ctx.javaVM = vm;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
        LOGD("GetEnv failed!");
        return JNI_ERR;
    }

    LOGD("JNI_OnLoad\n");

    loadJavaMethods(env);
    return JNI_VERSION_1_6;
}